using System;
using dnAnalytics.LinearAlgebra;
using dnAnalytics.LinearAlgebra.Solvers;
using dnAnalytics.LinearAlgebra.Solvers.Preconditioners;

namespace dnAnalytics.Examples.LinearAlgebra.Solvers
{
    /// <summary>
    /// Provides an example of the creation and use of the BiCGStab iterative solver
    /// </summary>
    public sealed class BicgstabSolver
    {
        /// <summary>
        /// Creates a new sparse matrix with zero values everywhere except
        /// on the diagonal where the values are equal to 1.0.
        /// </summary>
        private Matrix CreateMatrix(int size)
        {
            // Create the sparse matrix with the specified size
            Matrix matrix = MatrixBuilder.CreateMatrix(size, MatrixType.Sparse);
            // Add values to the matrix. For now we'll make the matrix
            // a unit matrix.
            for (int i = 0; i < size; i++)
            {
                matrix[i, i] = 1.0;
            }

            return matrix;
        }

        /// <summary>
        /// The main method that runs the BiCGStab iterative solver.
        /// </summary>
        public void UseSolver()
        {
            // Create a sparse matrix. For now the size will be 10 x 10 elements
            Matrix matrix = CreateMatrix(10);

            // Create the right hand side vector. The size is the same as the matrix
            // and all values will be 2.0.
            Vector rightHandSideVector = VectorBuilder.CreateVector(10, 2.0, VectorType.Dense);

            // Create a preconditioner. The possibilities are:
            // 1) No preconditioner - Create an instance of the UnitPreconditioner class.
            //    or create no preconditioner and use the appropriate constructor for the solver
            // 2) A simple diagonal preconditioner - Create an instance of the DiagonalPreconditioner class.
            // 3) A diagonal ILU preconditioner - Create an instance of the DiagonalIluFactorization class.
            // 4) A ILU preconditioner - Create an instance of the IluFactorization class.
            // 5) A ILU preconditioner with pivoting and droptolerances - Create an instance of the IlutpPreconditioner class.

            // Here we'll use the simple diagonal preconditioner.
            // We need a link to the matrix so the pre-conditioner can do it's work.
            IPreconditioner preconditioner = new DiagonalPreconditioner(matrix);

            // Create a new convergence monitor. This checks for convergence of the results of the
            // iterative matrix solver.
            // In this case:
            // We perform a maximum of 5000 iterations.
            // We will stop when the residual drops below 1e-14
            // And use the infinity norm as the appropriate norm for the convergence measurement.
            ConvergenceMonitor monitor = new ConvergenceMonitor(5000,
                                                                1e-14,
                                                                matrix.InfinityNorm());

            // Create the solver
            BiConjugateGradientStabilizedSolver solver =
                new BiConjugateGradientStabilizedSolver(matrix, preconditioner, monitor);

            // Now that all is set we can solve the matrix equation.
            Vector solutionVector = solver.Solve(rightHandSideVector);

            // Another way to get the values is by using the overloaded solve method
            // In this case the solution vector needs to be of the correct size.
            solver.Solve(rightHandSideVector, solutionVector);

            // Finally you can check the reason the solver finished the iterative process
            // by calling the SolutionStatus property on the convergence monitor
            switch (monitor.SolutionStatus)
            {
                case SolutionStatus.None:
                    Console.WriteLine("Oh oh, something went wrong. The iterative process was never started.");
                    break;
                case SolutionStatus.Converged:
                    Console.WriteLine("Yippee, the iterative process converged.");
                    break;
                case SolutionStatus.Diverged:
                    Console.WriteLine("I'm sorry the iterative process diverged.");
                    break;
                case SolutionStatus.IterationBoundsReached:
                    Console.WriteLine("Oh dear, the iterative process ran the maximum number of iterations.");
                    break;
            }
        }
    }
}